#include<linux/module.h>
#include<linux/netdevice.h>
#include<linux/ethtool.h>
#include<linux/mutex.h>
#include<linux/list.h>
#include<linux/rculist.h>
#include<linux/spinlock.h>
#include<linux/pci.h>
#include<net/dcbnl.h>
#include<net/ipv6.h>
#include<net/addrconf.h>
#include<linux/if_ether.h>
#include<rdma/ib_verbs.h>
#include<rdma/ib_user_verbs.h>
#include<rdma/ib_umem.h>
#include<rdma/ib_addr.h>

#include "visre.h"
#include "visre_verbs.h"

static const char * const rxe_counter_name[] = {
    [RXE_CNT_SENT_PKTS]           =  "isent_pkts",
    [RXE_CNT_RCVD_PKTS]           =  "rcvd_pkts",
    [RXE_CNT_DUP_REQ]             =  "duplicate_request",
    [RXE_CNT_OUT_OF_SEQ_REQ]      =  "out_of_seq_request",
    [RXE_CNT_RCV_RNR]             =  "rcvd_rnr_err",
    [RXE_CNT_SND_RNR]             =  "send_rnr_err",
    [RXE_CNT_RCV_SEQ_ERR]         =  "rcvd_seq_err",
    [RXE_CNT_COMPLETER_SCHED]     =  "ack_deferred",
    [RXE_CNT_RETRY_EXCEEDED]      =  "retry_exceeded_err",
    [RXE_CNT_RNR_RETRY_EXCEEDED]  =  "retry_rnr_exceeded_err",
    [RXE_CNT_COMP_RETRY]          =  "completer_retry_err",
    [RXE_CNT_SEND_ERR]            =  "send_err",
    [RXE_CNT_LINK_DOWNED]         =  "link_downed",
    [RXE_CNT_RDMA_SEND]           =  "rdma_sends",
    [RXE_CNT_RDMA_RECV]           =  "rdma_recvs",
};

struct rdma_hw_stats *vis_ib_alloc_hw_port_stats(
        struct ib_device *ibdev, u32 port_num)
{
    BUILD_BUG_ON(ARRAY_SIZE(rxe_counter_name) != RXE_NUM_OF_COUNTERS);
    return rdma_alloc_hw_stats_struct(rxe_counter_name,
            ARRAY_SIZE(rxe_counter_name), 10);
}

static struct ib_mr *vis_alloc_mr(struct ib_pd *ibpd, 
        enum ib_mr_type mr_type, u32 max_num_sg)
{
    return NULL;
}

static int vis_alloc_mw(struct ib_mw *ibmw, 
        struct ib_udata *udata)
{
    return 0;
}

static int vis_alloc_pd(struct ib_pd *ibpd, 
        struct ib_udata *udata)
{
    return 0;
}

static int vis_alloc_ucontext(struct ib_ucontext *ibuc,
        struct ib_udata *udata)
{
    return 0;
}

static int vis_attach_mcast(struct ib_qp *ibqp,
        union ib_gid *mgid, u16 mlid)
{
    return 0;
}

static int vis_create_ah(struct ib_ah *ibah,
        struct rdma_ah_init_attr *init_attr,
        struct ib_udata *udata)
{
    return 0;
}

static int vis_create_cq(struct ib_cq *ibcq, 
        const struct ib_cq_init_attr *attr, 
        struct ib_udata *udata)
{
    return 0;
}

static int vis_create_qp(struct ib_qp *ibqp, 
        struct ib_qp_init_attr *init,
        struct ib_udata *udata)
{
    return 0;
}

static int vis_create_srq(struct ib_srq *ibsrq, 
        struct ib_srq_init_attr *init,
        struct ib_udata *udata)
{
    return 0;
}

static void vis_dealloc(struct ib_device *ib_dev)
{
}

static int vis_dealloc_mw(struct ib_mw *ibmw)
{
    return 0;
}

static int vis_dealloc_pd(struct ib_pd *ibpd,
        struct ib_udata *udata)
{
    return 0;
}

static void vis_dealloc_ucontext(struct ib_ucontext *ibuc)
{
}

static int vis_dereg_mr(struct ib_mr *ibmr, 
        struct ib_udata *udata)
{
    return 0;
}

static int vis_destroy_ah(struct ib_ah *ibah, u32 flags)
{
    return 0;
}

static int vis_destroy_cq(struct ib_cq *ibcq, 
        struct ib_udata *udata)
{
    return 0;
}

static int vis_destroy_qp(struct ib_qp *ibqp,
        struct ib_udata *udata)
{
    return 0;
}

static int vis_destroy_srq(struct ib_srq *ibsrq,
        struct ib_udata *udata)
{
    return 0;
}

static int vis_detach_mcast(struct ib_qp *ibqp, 
        union ib_gid *mgid, u16 mlid)
{
    return 0;
}

static ssize_t parent_show(struct device *device,
        struct device_attribute *attr, char *buf)
{
    #if 0
    struct vis_re *vis =
        rdma_device_to_drv_device(device, struct vis_re, ibdev);

    return sysfs_emit(buf, "%s\n", vis_parent_name(vis, 1));
    #endif
    return 0;
}

static DEVICE_ATTR_RO(parent);

static struct attribute *vis_dev_attributes[] = {
    &dev_attr_parent.attr,
    NULL
};

static const struct attribute_group vis_attr_group = {
    .attrs = vis_dev_attributes,
};

static int vis_enable_driver(struct ib_device *ib_dev)
{
    return 0;
}

static struct ib_mr *vis_get_dma_mr(struct ib_pd *ibpd, int access)
{
    return NULL;
}

static int vis_ib_get_hw_stats(struct ib_device *ibdev,
        struct rdma_hw_stats *stats,
        u32 port, int index)
{
    if (!port || !stats)
        return -EINVAL;

    return ARRAY_SIZE(rxe_counter_name);
}

static enum rdma_link_layer vis_get_link_layer(
        struct ib_device *dev, u32 port_num)
{
    return IB_LINK_LAYER_ETHERNET;
}

static int vis_port_immutable(struct ib_device *dev, u32 port_num,
        struct ib_port_immutable *immutable)
{
    return 0;
}

static int vis_mmap(struct ib_ucontext *context, 
        struct vm_area_struct *vma)
{
    return 0;
}

static int vis_map_mr_sg(struct ib_mr *ibmr, 
        struct scatterlist *sg,
        int sg_nents, unsigned int *sg_offset)
{
    return 0;
}

static int vis_modify_ah(struct ib_ah *ibah, 
        struct rdma_ah_attr *attr)
{
    return 0;
}

static int vis_modify_device(struct ib_device *dev,
        int mask, struct ib_device_modify *attr)
{
    return 0;
}

static int vis_modify_port(struct ib_device *dev,
        u32 port_num, int mask, 
        struct ib_port_modify *attr)
{
    return 0;
}

static int vis_modify_qp(struct ib_qp *ibqp,
        struct ib_qp_attr *attr,
        int mask, struct ib_udata *udata)
{
    return 0;
}

static int vis_modify_srq(struct ib_srq *ibsrq, 
        struct ib_srq_attr *attr,
        enum ib_srq_attr_mask mask,
        struct ib_udata *udata)
{
    return 0;
}

static int vis_peek_cq(struct ib_cq *ibcq, int wc_cnt)
{
    return 0;
}

static int vis_poll_cq(struct ib_cq *ibcq, 
        int num_entries, struct ib_wc *wc)
{
    return 0;
}

static int vis_post_recv(struct ib_qp *ibqp, 
        const struct ib_recv_wr *wr,
        const struct ib_recv_wr **bad_wr)
{
    return 0;
}

static int vis_post_send(struct ib_qp *ibqp, 
        const struct ib_send_wr *wr,
        const struct ib_send_wr **bad_wr)
{
    return 0;
}

static int vis_post_srq_recv(struct ib_srq *ibsrq, 
        const struct ib_recv_wr *wr,
        const struct ib_recv_wr **bad_wr)
{
    return 0;
}

static int vis_query_ah(struct ib_ah *ibah, 
        struct rdma_ah_attr *attr)
{
    return 0;
}

static int vis_query_device(struct ib_device *dev,
        struct ib_device_attr *attr,
        struct ib_udata *uhw)
{
    return 0;
}

static int vis_query_pkey(struct ib_device *device,
        u32 port_num, u16 index, u16 *pkey)
{
    return 0;
}

static int vis_query_port(struct ib_device *dev,
        u32 port_num, struct ib_port_attr *attr)
{
    return 0;
}

static int vis_query_qp(struct ib_qp *ibqp,
        struct ib_qp_attr *attr,
        int mask, struct ib_qp_init_attr *init)
{
    return 0;
}

static int vis_query_srq(struct ib_srq *ibsrq,
        struct ib_srq_attr *attr)
{
    return 0;
}

static struct ib_mr *vis_reg_user_mr(struct ib_pd *ibpd,
        u64 start, u64 length, u64 iova,
        int access, struct ib_udata *udata)
{
    return 0;
}

static int vis_req_notify_cq(struct ib_cq *ibcq, 
        enum ib_cq_notify_flags flags)
{
    return 0;
}

static int vis_resize_cq(struct ib_cq *ibcq,
        int cqe, struct ib_udata *udata)
{
    return 0;
}

static const struct ib_device_ops vis_dev_ops = {
    .owner = THIS_MODULE,
    .driver_id = RDMA_DRIVER_RXE,
    .uverbs_abi_ver = VIS_UVERBS_ABI_VERSION,

    .alloc_hw_port_stats = vis_ib_alloc_hw_port_stats,
    .alloc_mr = vis_alloc_mr,
    .alloc_mw = vis_alloc_mw,
    .alloc_pd = vis_alloc_pd,
    .alloc_ucontext = vis_alloc_ucontext,
    .attach_mcast = vis_attach_mcast,
    .create_ah = vis_create_ah,
    .create_cq = vis_create_cq,
    .create_qp = vis_create_qp,
    .create_srq = vis_create_srq,
    .create_user_ah = vis_create_ah,
    .dealloc_driver = vis_dealloc,
    .dealloc_mw = vis_dealloc_mw,
    .dealloc_pd = vis_dealloc_pd,
    .dealloc_ucontext = vis_dealloc_ucontext,
    .dereg_mr = vis_dereg_mr,
    .destroy_ah = vis_destroy_ah,
    .destroy_cq = vis_destroy_cq,
    .destroy_qp = vis_destroy_qp,
    .destroy_srq = vis_destroy_srq,
    .detach_mcast = vis_detach_mcast,
    .device_group = &vis_attr_group,
    .enable_driver = vis_enable_driver,
    .get_dma_mr = vis_get_dma_mr,
    .get_hw_stats = vis_ib_get_hw_stats,
    .get_link_layer = vis_get_link_layer,
    .get_port_immutable = vis_port_immutable,
    .map_mr_sg = vis_map_mr_sg,
    .mmap = vis_mmap,
    .modify_ah = vis_modify_ah,
    .modify_device = vis_modify_device,
    .modify_port = vis_modify_port,
    .modify_qp = vis_modify_qp,
    .modify_srq = vis_modify_srq,
    .peek_cq = vis_peek_cq,
    .poll_cq = vis_poll_cq,
    .post_recv = vis_post_recv,
    .post_send = vis_post_send,
    .post_srq_recv = vis_post_srq_recv,
    .query_ah = vis_query_ah,
    .query_device = vis_query_device,
    .query_pkey = vis_query_pkey,
    .query_port = vis_query_port,
    .query_qp = vis_query_qp,
    .query_srq = vis_query_srq,
    .reg_user_mr = vis_reg_user_mr,
    .req_notify_cq = vis_req_notify_cq,
    .resize_cq = vis_resize_cq,

    INIT_RDMA_OBJ_SIZE(ib_ah, vis_ah, ibah),
    INIT_RDMA_OBJ_SIZE(ib_cq, vis_cq, ibcq),
    INIT_RDMA_OBJ_SIZE(ib_pd, vis_pd, ibpd),
    INIT_RDMA_OBJ_SIZE(ib_qp, vis_qp, ibqp),
    INIT_RDMA_OBJ_SIZE(ib_srq, vis_srq, ibsrq),
    INIT_RDMA_OBJ_SIZE(ib_ucontext, vis_ucontext, ibuc),
    INIT_RDMA_OBJ_SIZE(ib_mw, vis_mw, ibmw),
};

int vis_register_device(struct vis_re *vis)
{
    int err = 0;
    struct ib_device *dev = &vis->ibdev;

    strscpy(dev->node_desc, "visre", sizeof(dev->node_desc));

    dev->node_type = RDMA_NODE_IB_CA;
    dev->phys_port_cnt = 1;
    dev->num_comp_vectors = num_possible_cpus();
    dev->local_dma_lkey = 0;
    addrconf_addr_eui48((unsigned char *)&dev->node_guid,
            vis->ndev->dev_addr);
    dev->uverbs_cmd_mask |= BIT_ULL(IB_USER_VERBS_CMD_POST_SEND) |
            BIT_ULL(IB_USER_VERBS_CMD_REQ_NOTIFY_CQ);

    ib_set_device_ops(dev, &vis_dev_ops);
    err = ib_device_set_netdev(&vis->ibdev, vis->ndev, 1);
    if (err) {
        pr_err("ib_device_set_netdev err\n");
        return err;
    }

    err = ib_register_device(dev, "visre", NULL);
    if (err) {
        pr_err("%s failed with error %d\n", __func__, err);
        return err;
    }
    return 0;
}
